package log

import (
	"bytes"
	"io"
	"runtime"
	"sync"
)

type message struct {
	buf  BytesReader
	lv   Level
	size int
}

func (m *message) write(w LogWriter) {
	m.buf.ReadBytes(m.size, func(data []byte) {
		w.WriteLog(m.lv, data)
	})
}

type logger struct {
	m    sync.Mutex
	buf  *RingBuffer   //buf池
	msgs chan *message //日志缓存
	f    Formatter     //格式化日志接口
	w    LogWriter     //写日志接口
	mlvs Levels        //不能丢弃的日志
}

type LogOption func(*logger)

func LogSetWriter(writer LogWriter) LogOption {
	return func(l *logger) {
		l.w = writer
	}
}

func LogFormatter(formatter Formatter) LogOption {
	return func(l *logger) {
		l.f = formatter
	}
}

func LogMustLevels(lvs Levels) LogOption {
	return func(l *logger) {
		l.mlvs = lvs
	}
}

func LogCache(size int) LogOption {
	return func(l *logger) {
		if size < 1024*1024 {
			size = 1024 * 1024
		}
		l.buf = NewRingBuffer(size)
	}
}
func NewLogger(opts ...LogOption) Logger {
	a := &logger{
		mlvs: Lv_Error,
		msgs: make(chan *message, 256),
	}
	for _, opt := range opts {
		opt(a)
	}
	if a.w == nil {
		a.w = NewConsoleWriter()
	}
	if a.f == nil {
		a.f = NewTextFormatter()
	}
	if a.buf == nil {
		a.buf = NewRingBuffer(1024 * 1024)
	}
	runtime.SetFinalizer(a, func(a *logger) {
		close(a.msgs)
	})
	go func(msgs <-chan *message) {
		for msg := range msgs {
			msg.write(a.w)
		}
		a.w.Close()
	}(a.msgs)
	return a
}

func (l *logger) Log(lv Level, args ...any) error {
	return l.LogDepth(2, lv, args...)
}

func (l *logger) Logf(lv Level, format string, args ...any) error {
	return l.LogDepthf(2, lv, format, args...)
}

func (l *logger) Logr(lv Level, r io.Reader) error {
	return l.LogDepthr(2, lv, r)
}

func (l *logger) LogDepth(calldepth int, lv Level, args ...any) error {
	if l.Enable(lv) {
		l.m.Lock()
		defer l.m.Unlock()
		size, err := l.GetFormatter().Sprint(calldepth+1, l.buf, lv, args...)
		if err != nil {
			if size > 0 {
				l.buf.BackWrite(size)
			}
			if l.mlvs.Enable(lv) {
				buf := make(ByteBuffer, 0)
				w := bytes.NewBuffer(buf)
				size, err = l.GetFormatter().Sprint(calldepth+1, w, lv, args...)
				if err != nil {
					//has a error
					return err
				}
				l.pushLog(&message{
					buf:  buf,
					lv:   lv,
					size: size,
				})
				return nil
			}
			return err
		}
		l.pushLog(&message{
			buf:  l.buf,
			lv:   lv,
			size: size,
		})
	}
	return nil
}

func (l *logger) LogDepthf(calldepth int, lv Level, format string, args ...any) error {
	if l.Enable(lv) {
		l.m.Lock()
		defer l.m.Unlock()
		size, err := l.GetFormatter().Sprintf(calldepth+1, l.buf, lv, format, args...)
		if err != nil {
			if size > 0 {
				l.buf.BackWrite(size)
			}
			if l.mlvs.Enable(lv) {
				buf := make(ByteBuffer, 0)
				w := bytes.NewBuffer(buf)
				size, err = l.GetFormatter().Sprintf(calldepth+1, w, lv, format, args...)
				if err != nil {
					//has a error
					return err
				}
				l.pushLog(&message{
					buf:  buf,
					lv:   lv,
					size: size,
				})
				return nil
			}
			return err
		}
		l.pushLog(&message{
			buf:  l.buf,
			lv:   lv,
			size: size,
		})
	}
	return nil
}

func (l *logger) LogDepthr(calldepth int, lv Level, r io.Reader) error {
	if l.Enable(lv) {
		l.m.Lock()
		defer l.m.Unlock()
		_, err := l.GetFormatter().Sprintr(calldepth+1, l.buf, lv, r)
		return err
	}
	return nil
}

func (l *logger) Enable(lv Level) bool {
	return l.w.IsWrite(lv)
}

func (l *logger) pushLog(msg *message) {
	select {
	case l.msgs <- msg:
	default:
		//保证error和fatal日志记录
		if l.mlvs.Enable(msg.lv) {
			l.msgs <- msg
		}
	}
}

func (l *logger) GetFormatter() Formatter {
	return l.f
}

func (l *logger) Close() error {
	l.m.Lock()
	defer l.m.Unlock()
	if l.mlvs != NOLOG {
		close(l.msgs)  //关闭缓冲数据
		l.mlvs = NOLOG //所有日志都丢弃
		l.msgs = make(chan *message)
	}
	return nil
}
